package com.unlcn.ils.kas.service.security.util;

import com.unlcn.ils.kas.service.security.config.JwtSettings;
import com.unlcn.ils.kas.service.security.domains.JwtUser;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by qichao on 2017/11/29.
 */
@Component
public class JwtTokenUtil implements Serializable {
    private static final long serialVersionUID = 2259414142929197254L;
    private static final String CLAIM_KEY_ACCOUNT = "sub";
    private static final String CLAIM_KEY_CREATED = "created";
    private static final String EXPIRATION = "exp";

    @Autowired
    private JwtSettings jwtSettings;

    public String getUserNameFromToken(String token) {
        String userName;
        try {
            final Claims claims = getClaimsFromToken(token);
            userName = claims.getSubject();
        } catch (Exception e) {
            userName = null;
        }
        return userName;
    }

    public Date getCreatedDateFromToken(String token) {
        Date created;
        try {
            final Claims claims = getClaimsFromToken(token);
            created = new Date((Long) claims.get(CLAIM_KEY_CREATED));
        } catch (Exception e) {
            created = null;
        }
        return created;
    }

    public Date getExpirationDateFromToken(String token) {
        Date expiration;
        try {
            final Claims claims = getClaimsFromToken(token);
            expiration = claims.getExpiration();
        } catch (Exception e) {
            expiration = null;
        }
        return expiration;
    }

    private Claims getClaimsFromToken(String token) {
        Claims claims;
        try {
            claims = Jwts.parser().setSigningKey(jwtSettings.getSecret()).parseClaimsJws(token).getBody();
        } catch (Exception e) {
            claims = null;
        }
        return claims;
    }

    private Date generateExpirationDate() {
        return new Date(System.currentTimeMillis() + jwtSettings.getExpiration() * 1000);
    }

    private Boolean isTokenExpired(String token) {
        final Date expiration = getExpirationDateFromToken(token);
        return expiration.before(new Date());
    }

    private Boolean isCreatedBeforeLastPasswordReset(Date created, Date lastPasswordReset) {
        return (lastPasswordReset != null && created.before(lastPasswordReset));
    }

    public String generateToken(JwtUser user) {
        Map<String, Object> claims = new HashMap<>();
        claims.put(CLAIM_KEY_ACCOUNT, user.getAccountCode());
        claims.put(CLAIM_KEY_CREATED, new Date());
        claims.put(EXPIRATION, generateExpirationDate());
        return Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.HS512, jwtSettings.getSecret()).compact();
    }

    public String buildHeadToken(JwtUser jwtUser) {
        return jwtSettings.getTokenHead() + generateToken(jwtUser);
    }

    public Boolean canTokenBeRefreshed(String token, Date lastPasswordReset) {
        final Date created = getCreatedDateFromToken(token);
        return !isCreatedBeforeLastPasswordReset(created, lastPasswordReset) && !isTokenExpired(token);
    }

    public Boolean validateToken(String token, UserDetails userDetails) {
        JwtUser user = (JwtUser) userDetails;
        final String username = getUserNameFromToken(token);
        return (username.equals(user.getAccountCode()) && !isTokenExpired(token));
    }

}
